package com.diagrammer.convertor
{
	import com.diagrammer.vo.LineVO;
	import com.diagrammer.vo.NodeVO;
	import com.diagrammer.vo.factory.IVOFactory;
	import com.diagrammer.vo.factory.VOFactory;
	
	/**
	 * 将节点和线条数据dataProvider转换成相应的VO
	 * 
	 * @author Dwei <b>2012-02-10</b>
	 * 
	 */	
	public class VOConvertor
	{
//============================================================================
//parameter
//============================================================================
		private const _nodes:Array = [];
		
		private const _nodesMap:Object = {};
		
		private const _nodesDeleted:Array = [];
		
		private const _lines:Array = [];
		
		private const _linesMap:Object = {};
		
		private const _linesDeleted:Array = [];
		
		private var _factory:IVOFactory;
		//field
		private var _nodesField:String;
		
		private var _linesField:String;
		
		private var _nodeIDField:String;
		
		private var _lineIDField:String;
		
		private var _lineFromIDField:String;
		
		private var _lineToIDField:String;
//============================================================================
//constructor
//============================================================================
		public function VOConvertor()
		{
			super();
			_factory = new VOFactory();
		}
//============================================================================
//public
//============================================================================
		/**
		 * 新增节点，在记录中添加一个节点VO
		 * @param value
		 * @return 新增的节点VO，若新增失败，返回null
		 * 
		 */		
		public function addNode(value:XML):NodeVO
		{
			//如果无数据，返回null
			if (!value) return null;
			//获取节点VO
			var result:NodeVO = factory.getNodeVO(value);
			if (!result)
			{
				trace ("新增节点VO失败：节点VO工厂不支持该数据-" + value.toXMLString());
				return null;
			}
			//初始化节点
			result.data = value;
			result.ID = String(value[nodeIDField]);
			//判断节点标识是否重复
			if (isNodeExist(result.ID))
			{
				trace ("新增节点VO失败：节点标识重复-" + factory.getNodeDataProvider(result, nodeIDField).toXMLString());
				return null;
			}
			//保存节点
			nodes.push(result.ID);
			nodesMap[result.ID] = result;
			trace ("新增NodeVO:" + factory.getNodeDataProvider(result, nodeIDField).toXMLString());
			return result;
		}
		
		/**
		 * 删除节点，删除记录中的节点VO
		 * @param id 节点标识
		 * 
		 */		
		public function removeNode(id:String):void
		{
			//如果无传入节点，直接返回
			if (!isNodeExist(id))
			{
				trace ("删除节点VO失败：节点VO不存在-" + id);
				return;
			}
			//保存删除节点
			nodesDeleted.push(nodesMap[id]);
			//删除节点
			nodes.splice(nodes.indexOf(id), 1);
			delete nodesMap[id];
			trace ("删除NodeVO:" + id);
		}
		
		/**
		 * 新增线条，在记录中添加一个线条VO
		 * @param value
		 * @return 新增的线条VO，若新增失败，返回null
		 * 
		 */		
		public function addLine(value:XML):LineVO
		{
			//如果无数据，返回null
			if (!value) return null;
			//获取线条VO
			var result:LineVO = factory.getLineVO(value);
			if (!result)
			{
				trace ("新增线条VO失败：线条VO工厂不支持该数据-" + value.toXMLString());
				return null;
			}
			//初始化线条
			result.data = value;
			result.ID = String(value[lineIDField]);
			result.fromID = String(value[lineFromIDField]);
			result.toID = String(value[lineToIDField]);
			//判断线条标识是否重复
			if (isLineExist(result.ID))
			{
				trace ("新增线条VO失败：线条标识或者起始和结束节点标识重复-" + factory.getLineDataProvider(result, lineIDField, lineFromIDField, lineToIDField) .toXMLString());
				return null;
			}
			//判断线条起始和结束节点标识是否重复
			if (isRelationExist(result.fromID, result.toID))
			{
				trace ("新增线条VO失败：线条起始和结束节点标识重复-" + factory.getLineDataProvider(result, lineIDField, lineFromIDField, lineToIDField) .toXMLString());
				return null;
			}
			//判断线条的起始和结束节点是否存在
			if (!isNodeExist(result.fromID) || !isNodeExist(result.toID))
			{
				trace ("新增线条VO失败：线条起始或者结束节点VO不存在-" + factory.getLineDataProvider(result, lineIDField, lineFromIDField, lineToIDField) .toXMLString());
				return null;
			}
			//保存线条
			lines.push(result.ID);
			linesMap[result.ID] = result;
			trace ("新增LineVO:" + factory.getLineDataProvider(result, lineIDField, lineFromIDField, lineToIDField) .toXMLString());
			return result;
		}
		
		/**
		 * 删除线条，删除记录中的线条VO
		 * @param id 线条标识
		 * 
		 */		
		public function removeLine(id:String):void
		{
			//如果无传入线条，直接返回
			if (!isLineExist(id))
			{
				trace ("删除线条VO失败：线条VO不存在-" + id);
				return;
			}
			//保存删除线条
			linesDeleted.push(linesMap[id]);
			//删除线条
			lines.splice(lines.indexOf(id), 1);
			delete linesMap[id];
			trace ("删除LineVO:" + id);
		}
		
		/**
		 * 判断是否存在与传入的节点标识相同的节点
		 * @param id 节点标识
		 * @return 
		 * 
		 */		
		public function isNodeExist(id:String):Boolean
		{
			return nodes.indexOf(id) != -1;
		}
		
		/**
		 * 判断是否存在与传入线条的标识相同的线条
		 * @param id 线条标识
		 * @return 
		 * 
		 */		
		public function isLineExist(id:String):Boolean
		{
			return lines.indexOf(id) != -1;
		}
		
		/**
		 * 判断是否存在与传入线条的起始和结束节点相同的线条
		 * @param fromId 线条起始节点标识
		 * @param toId 线条结束节点标识
		 * @return 
		 * 
		 */		
		public function isRelationExist(fromId:String, toId:String):Boolean
		{
			var key:String;
			var line:LineVO;
			for (key in linesMap)
			{
				line = linesMap[key];
				if ((line.fromID == fromId) && (line.toID == toId)) return true;
			}
			return false;
		}
		
		/**
		 * 根据线条VO，建立线条关联的起始和结束节点的关系(NodeVO.lines、parents、children)
		 * @param id 线条标识
		 * 
		 */		
		public function relateNodes(id:String):void
		{
			//如果传入的线条标识不存在，直接返回
			if (!isLineExist(id)) return;
			//传入的线条VO
			var line:LineVO = linesMap[id];
			//线条关联的起始节点VO
			var fromNode:NodeVO = nodesMap[line.fromID];
			//如果已添加过该条关系，直接返回
			if (fromNode.lines.indexOf(id) != -1) return;
			if (line.fromID == line.toID)//线条起始和结束节点重复
			{
				fromNode.lines.push(id);
				return;
			}
			//线条关联的结束节点VO
			var toNode:NodeVO = nodesMap[line.toID];
			//初始起始节点关系
			fromNode.children.push(line.toID);
			fromNode.lines.push(id);
			//初始化结束节点关系
			toNode.parent.push(line.fromID);
			toNode.lines.push(id);
		}
		
		/**
		 * 根据线条VO，取消线条关联的起始和结束节点的关系(NodeVO.lines、parents、children)
		 * @param id 线条标识
		 * 
		 */		
		public function unRelateNodes(id:String):void
		{
			//如果传入的线条标识不存在，直接返回
			if (!isLineExist(id)) return;
			//传入的线条VO
			var line:LineVO = linesMap[id];
			//线条关联的起始节点VO
			var fromNode:NodeVO = nodesMap[line.fromID];
			//如果未设置过或者已取消该条关系，直接返回
			if (fromNode.lines.indexOf(id) == -1) return;
			if (line.fromID == line.toID)
			{
				fromNode.lines.splice(fromNode.lines.indexOf(id), 1);
				return;
			}
			//线条关联结束节点VO
			var toNode:NodeVO = nodesMap[line.toID];
			//更新起始节点
			fromNode.children.splice(fromNode.children.indexOf(line.toID), 1);
			fromNode.lines.splice(fromNode.lines.indexOf(id), 1);
			//更新结束节点
			toNode.parent.splice(toNode.parent.indexOf(line.fromID), 1);
			toNode.lines.splice(toNode.lines.indexOf(id), 1);
		}
//============================================================================
//protected
//============================================================================
		/**
		 * 清空数据
		 * 
		 */		
		protected function clear():void
		{
			var key:String;
			for each (key in nodes)
			{
				delete nodesMap[key];
			}
			nodes.splice(0, nodes.length);
			nodesDeleted.splice(0, nodesDeleted.length);
			
			for each (key in lines)
			{
				delete linesMap[key];
			}
			lines.splice(0, lines.length);
			linesDeleted.splice(0, linesDeleted.length);
		}
		
		/**
		 * 将节点数据转换成节点VO
		 * @param value &lt;node .../&gt;...&lt;node .../&gt;
		 * 
		 */		
		protected function convertNodes(value:XMLList):void
		{
			var datum:XML;
			for each (datum in value)
			{
				addNode(datum);
			}
		}
		
		/**
		 * 将线条数据转换成线条VO，并初始化节点VO之间的关系
		 * @param value &lt;line .../&gt;...&lt;line .../&gt;
		 * 
		 */		
		protected function convertLines(value:XMLList):void
		{
			var datum:XML;
			var line:LineVO;
			for each (datum in value)
			{
				line = addLine(datum);
				if (line) relateNodes(line.ID);
			}
		}
//============================================================================
//getter/setter
//============================================================================

		/**
		 * 拓扑图数据<br/>
		 * &lt;data&gt;<br/>
		 * &lt;nodes&gt;<br/>
		 * &lt;node .../&gt;<br/>
		 * ...<br/>
		 * &lt;node .../&gt;<br/>
		 * &lt;/nodes&gt;<br/>
		 * &lt;lines&gt;<br/>
		 * &lt;line .../&gt;<br/>
		 * ...<br/>
		 * &lt;line .../&gt;<br/>
		 * &lt;/lines&gt;<br/>
		 * &lt;/data&gt;
		 */		
		public function get dataProvider():XML
		{
			var result:XML = <data/>;
			var key:String;
			var node:NodeVO;
			var nodesDP:XML = <{nodesField}/>;
			for each (key in nodes)//获取节点数据
			{
				node = nodesMap[key];
				nodesDP.appendChild(factory.getNodeDataProvider(node, nodeIDField));
			}
			var line:LineVO;
			var linesDP:XML = <{linesField}/>;
			for each (key in lines)//获取线条数据
			{
				line = linesMap[key];
				linesDP.appendChild(factory.getLineDataProvider(line, lineIDField, lineFromIDField, lineToIDField));
			}
			result.appendChild(nodesDP);
			result.appendChild(linesDP);
			return result;
		}

		/**
		 * @private
		 */		
		public function set dataProvider(value:XML):void
		{
			clear();
			if (!value) return;//如果未传入数据，先清空现有数据，再返回
			//节点数据
			var nodesDP:XMLList = value[nodesField] as XMLList;
			if (nodesDP.length() == 0) return;//如果无节点数据，直接返回
			convertNodes(nodesDP.elements());
			if (nodes.length == 0) return;//如果无有效节点VO，直接返回
			//线条数据
			var linesDP:XMLList = value[linesField] as XMLList;
			if (linesDP.length() == 0) return;//如果无线条数据，直接返回
			convertLines(linesDP.elements());
		}

		[ArrayElementType("String")]
		/**
		 * 所有节点标识集合
		 */
		public function get nodes():Array
		{
			return _nodes;
		}

		/**
		 * 所有节点标识-节点VO映射集合
		 */
		public function get nodesMap():Object
		{
			return _nodesMap;
		}

		[ArrayElementType("com.diagrammer.vo.NodeVO")]
		/**
		 * 
		 */		
		public function get nodesDeleted():Array
		{
			return _nodesDeleted;
		}

		[ArrayElementType("String")]
		/**
		 * 所有线条标识集合
		 */
		public function get lines():Array
		{
			return _lines;
		}

		/**
		 * 所有线条标识-线条VO映射集合
		 */
		public function get linesMap():Object
		{
			return _linesMap;
		}

		[ArrayElementType("com.diagrammer.vo.LineVO")]
		/**
		 * 所有删除的线条
		 */		
		public function get linesDeleted():Array
		{
			return _linesDeleted;
		}

		/**
		 * 创建节点VO对象的工厂类，忽略null，默认-com.diagrammer.vo.factory.VOFactory
		 */
		public function get factory():IVOFactory
		{
			return _factory;
		}

		/**
		 * @private
		 */
		public function set factory(value:IVOFactory):void
		{
			if (!value) return;
			_factory = value;
		}
		//field
		/**
		 * 节点数据根节点名称，忽略null和""，默认-nodes
		 */
		public function get nodesField():String
		{
			return _nodesField?_nodesField:"nodes";
		}

		/**
		 * @private
		 */
		public function set nodesField(value:String):void
		{
			_nodesField = value;
		}

		/**
		 * 线条数据根节点名称，忽略null和""，默认-lines
		 */
		public function get linesField():String
		{
			return _linesField?_linesField:"lines";
		}

		/**
		 * @private
		 */
		public function set linesField(value:String):void
		{
			_linesField = value;
		}

		/**
		 * 节点标识属性名称，忽略null和""，默认-<b>at</b>id(<b>at</b>为符号：shift+2)
		 */
		public function get nodeIDField():String
		{
			return _nodeIDField?_nodeIDField:"@id";
		}

		/**
		 * @private
		 */
		public function set nodeIDField(value:String):void
		{
			_nodeIDField = value;
		}

		/**
		 * 线条标识属性名称，忽略null和""，默认-<b>at</b>id(<b>at</b>为符号：shift+2)
		 */
		public function get lineIDField():String
		{
			return _lineIDField?_lineIDField:"@id";
		}

		/**
		 * @private
		 */
		public function set lineIDField(value:String):void
		{
			_lineIDField = value;
		}

		/**
		 * 线条起始节点标识属性名称，忽略null和""，默认-<b>at</b>fromId(<b>at</b>为符号：shift+2)
		 */
		public function get lineFromIDField():String
		{
			return _lineFromIDField?_lineFromIDField:"@fromId";
		}

		/**
		 * @private
		 */
		public function set lineFromIDField(value:String):void
		{
			_lineFromIDField = value;
		}

		/**
		 * 线条结束节点标识属性名称，忽略null和""，默认-<b>at</b>toId(<b>at</b>为符号：shift+2)
		 */
		public function get lineToIDField():String
		{
			return _lineToIDField?_lineToIDField:"@toId";
		}

		/**
		 * @private
		 */
		public function set lineToIDField(value:String):void
		{
			_lineToIDField = value;
		}


	}
}